#include "p8x32a.h"
#include "p8x32acogpanel.h"
#include <QFormLayout>

P8x32aCogPanel::P8x32aCogPanel(QWidget *parent) :
    QWidget(parent)
{
    QHBoxLayout *ilayout = new QHBoxLayout();
    QFormLayout *slayout = new QFormLayout();
    QHBoxLayout *hlayout = new QHBoxLayout();

    int ht = 16;
    int wd = 75;

    special.setLayout(slayout);

    slayout->addRow("PAR  1f0",&parValue);
    slayout->addRow("CNT  1f1",&cntValue);
    slayout->addRow("INA  1f2",&inaValue);
    slayout->addRow("INB  1f3",&inbValue);
    slayout->addRow("OUTA 1f4",&outaValue);
    slayout->addRow("OUTB 1f5",&outbValue);
    slayout->addRow("DIRA 1f6",&diraValue);
    slayout->addRow("DIRB 1f7",&dirbValue);
    slayout->addRow("CTRA 1f8",&ctraValue);
    slayout->addRow("CTRB 1f9",&ctrbValue);
    slayout->addRow("FRQA 1fa",&frqaValue);
    slayout->addRow("FRQB 1fb",&frqbValue);
    slayout->addRow("PHSA 1fc",&phsaValue);
    slayout->addRow("PHSB 1fd",&phsbValue);
    slayout->addRow("VCFG 1fe",&vcfgValue);
    slayout->addRow("VSCL 1ff",&vsclValue);

    parValue.setMaximumHeight(ht);
    cntValue.setMaximumHeight(ht);
    inaValue.setMaximumHeight(ht);
    inbValue.setMaximumHeight(ht);
    outaValue.setMaximumHeight(ht);
    outbValue.setMaximumHeight(ht);
    diraValue.setMaximumHeight(ht);
    dirbValue.setMaximumHeight(ht);
    ctraValue.setMaximumHeight(ht);
    ctrbValue.setMaximumHeight(ht);
    frqaValue.setMaximumHeight(ht);
    frqbValue.setMaximumHeight(ht);
    phsaValue.setMaximumHeight(ht);
    phsbValue.setMaximumHeight(ht);
    vcfgValue.setMaximumHeight(ht);
    vsclValue.setMaximumHeight(ht);

    parValue.setMinimumWidth(wd);
    cntValue.setMinimumWidth(wd);
    inaValue.setMinimumWidth(wd);
    inbValue.setMinimumWidth(wd);
    outaValue.setMinimumWidth(wd);
    outbValue.setMinimumWidth(wd);
    diraValue.setMinimumWidth(wd);
    dirbValue.setMinimumWidth(wd);
    ctraValue.setMinimumWidth(wd);
    ctrbValue.setMinimumWidth(wd);
    frqaValue.setMinimumWidth(wd);
    frqbValue.setMinimumWidth(wd);
    phsaValue.setMinimumWidth(wd);
    phsbValue.setMinimumWidth(wd);
    vcfgValue.setMinimumWidth(wd);
    vsclValue.setMinimumWidth(wd);

    int codewidth = 4;
    hlayout->addWidget(&code,codewidth);
    hlayout->addWidget(&special,1);

    layout.addLayout(hlayout);

    pcLabel.setText("PC");
    insLabel.setText("INS");
    cndLabel.setText("COND");
    zLabel.setText("Z");
    cLabel.setText("C");
    srcLabel.setText("S");
    dstLabel.setText("D");
    resLabel.setText("R");

#if defined(Q_OS_WIN)
    pcLabel.setMaximumHeight(ht-5);
    insLabel.setMaximumHeight(ht-5);
    cndLabel.setMaximumHeight(ht-5);
    zLabel.setMaximumHeight(ht-5);
    cLabel.setMaximumHeight(ht-5);
    srcLabel.setMaximumHeight(ht-5);
    dstLabel.setMaximumHeight(ht-5);
    resLabel.setMaximumHeight(ht-5);
#endif

    ilayout->addWidget(&pcLabel);
    ilayout->addWidget(&pcValue);
    ilayout->addWidget(&insLabel);
    ilayout->addWidget(&insValue);
    ilayout->addWidget(&cndLabel);
    ilayout->addWidget(&cndValue);
    ilayout->addWidget(&zLabel);
    ilayout->addWidget(&zValue);
    ilayout->addWidget(&cLabel);
    ilayout->addWidget(&cValue);
    ilayout->addWidget(&srcLabel);
    ilayout->addWidget(&srcValue);
    ilayout->addWidget(&dstLabel);
    ilayout->addWidget(&dstValue);
    ilayout->addWidget(&resLabel);
    ilayout->addWidget(&resValue);

    pcValue.setMaximumHeight(ht);
    insValue.setMaximumHeight(ht);
    cndValue.setMaximumHeight(ht);
    zValue.setMaximumHeight(ht);
    zValue.setMaximumWidth(75);
    cValue.setMaximumHeight(ht);
    cValue.setMaximumWidth(75);
    srcValue.setMaximumHeight(ht);
    dstValue.setMaximumHeight(ht);
    resValue.setMaximumHeight(ht);

    pcLabel.setAlignment(Qt::AlignRight);
    insLabel.setAlignment(Qt::AlignRight);
    cndLabel.setAlignment(Qt::AlignRight);
    zLabel.setAlignment(Qt::AlignRight);
    cLabel.setAlignment(Qt::AlignRight);
    srcLabel.setAlignment(Qt::AlignRight);
    dstLabel.setAlignment(Qt::AlignRight);
    resLabel.setAlignment(Qt::AlignRight);

    pcValue.setAlignment(Qt::AlignRight);
    insValue.setAlignment(Qt::AlignRight);
    cndValue.setAlignment(Qt::AlignRight);
    zValue.setAlignment(Qt::AlignRight);
    cValue.setAlignment(Qt::AlignRight);
    srcValue.setAlignment(Qt::AlignRight);
    dstValue.setAlignment(Qt::AlignRight);
    resValue.setAlignment(Qt::AlignRight);

    instructions.setLayout(ilayout);
    //layout.addWidget(&instructions);

    setLayout(&layout);

    connect(&code,SIGNAL(cellClicked(int,int)),this,SLOT(cellClicked(int,int)));
}

P8x32aCogPanel::~P8x32aCogPanel()
{
    brkHash.clear();
    clearCogMem();
}

void P8x32aCogPanel::clearBreakpoints()
{
    int pc;
    bool ok;
    QList<QString> keys = brkHash.keys();
    foreach(QString key, keys) {
        pc = key.toInt(&ok);
        if(ok && pc < code.rowCount()) {
            code.item(pc,0)->setText("");
        }
        brkHash.remove(key);
    }
}

void P8x32aCogPanel::cellClicked(int r, int c)
{
    c = 0;
    if(code.item(r,c)->text().length() == 0) {
        code.item(r,c)->setText("o");
        brkHash[QString("%1").arg(r)] = -1;
        emit setBreakpoint(r, cog);
    } else {
        code.item(r,c)->setText("");
        brkHash[QString("%1").arg(r)] = 0;
        emit clearBreakpoint(r, cog);
    }
}

void P8x32aCogPanel::clearCogMem()
{
    if(code.rowCount() > 0) {
        for(int n = code.rowCount()-1; n > -1; n--) {
            for(int m = code.columnCount()-1; m > -1; m--) {
                QTableWidgetItem *t = code.item(n,m);
                delete t;
                code.removeColumn(m);
            }
            code.removeRow(n);
        }
    }
    code.clear();
}

void P8x32aCogPanel::addCogInstructions(QList<uint> codeList)
{
    int count = codeList.count();
    QStringList labels;

    code.setRowCount(count);

    QStringList vlabels;
    for(int n = 0; n < count; n++)
        vlabels.append(QString("$%1").arg(n,3,16,QChar(QLatin1Char('0'))));
    code.setVerticalHeaderLabels(vlabels);

    labels.append("B");
    labels.append("Register");
    labels.append("Instruction");
    //labels.append("OP");
    labels.append("Condition");
    labels.append("WZ");
    labels.append("WC");
    labels.append("WR");
    labels.append("DST");
    //labels.append("I");
    labels.append("SRC");
    labels.append("C");
    labels.append("Z");
    labels.append("Result");
    code.setColumnCount(labels.count());
    code.setHorizontalHeaderLabels(labels);

    QFontMetrics fm(code.font());
    int pad = 4;
    code.setColumnWidth(CODE_BRK,pad+fm.width("W"));
    code.setColumnWidth(CODE_INS,pad+fm.width("   Register    "));
    code.setColumnWidth(CODE_NAME,pad+fm.width(" Instruction "));
    //code.setColumnWidth(CODE_OP,pad+fm.width("  OP  "));
    code.setColumnWidth(CODE_CND,pad+fm.width("if_nc_and_nz "));
    code.setColumnWidth(CODE_WZ,pad+fm.width(" WZ"));
    code.setColumnWidth(CODE_WC,pad+fm.width(" WC"));
    code.setColumnWidth(CODE_WR,pad+fm.width(" WR"));
    code.setColumnWidth(CODE_DST,pad+fm.width("  DST  "));
    //code.setColumnWidth(CODE_IMM,pad+fm.width(" #"));
    code.setColumnWidth(CODE_SRC,pad+fm.width(" #$SRC  "));
    code.setColumnWidth(CODE_CRES,pad+fm.width(" C"));
    code.setColumnWidth(CODE_ZRES,pad+fm.width(" Z"));
    code.setColumnWidth(CODE_DRES,pad+fm.width("  Destination  "));

    for(int n = 0; n < count; n++) {
        setCodeLine(n, codeList.at(n), codeList);
    }
}

void P8x32aCogPanel::setCodeLine(int pc, uint instruction, QList<uint> codeList, CogInfo *info)
{
    uint ins_cnd = (instruction & P8x32a::COND_MASK)    >> P8x32a::COND_START;
    uint ins_op  = (instruction & P8x32a::INS_MASK)     >> P8x32a::INS_START;
    uint ins_zcri= ((instruction & P8x32a::ZCRI_MASK)   >> P8x32a::ZCRI_START);
    uint ins_wz  = (((instruction & P8x32a::ZCRI_WZ)    >> P8x32a::ZCRI_START) >> 3) & 1;
    uint ins_wc  = (((instruction & P8x32a::ZCRI_WC)    >> P8x32a::ZCRI_START) >> 2) & 1;
    uint ins_wr  = (((instruction & P8x32a::ZCRI_WR)    >> P8x32a::ZCRI_START) >> 1) & 1;
    uint ins_imm = (((instruction & P8x32a::ZCRI_IMMED) >> P8x32a::ZCRI_START) >> 0) & 1;
    uint ins_src = (instruction & P8x32a::SRC_MASK)     >> P8x32a::SRC_START;
    uint ins_dst = (instruction & P8x32a::DST_MASK)     >> P8x32a::DST_START;

    QString cres;
    QString zres;
    QString dres;

    if(info != 0) {
        cres = QString("%1").arg(info->c ? 1 : 0,1,2);
        zres = QString("%1").arg(info->z ? 1 : 0,1,2);
        dres = QString("$%1").arg(info->result,8,16,QChar(QLatin1Char('0')));
    }
    else {
        if(code.item(pc,CODE_CRES) != 0)
            cres = code.item(pc,CODE_CRES)->text();
        if(code.item(pc,CODE_ZRES) != 0)
            zres = code.item(pc,CODE_ZRES)->text();
        if(code.item(pc,CODE_DRES) != 0)
            dres = code.item(pc,CODE_DRES)->text();
    }

    QTableWidgetItem *brk;
    if(brkHash[QString("%1").arg(pc)] != 0)
        brk = new QTableWidgetItem("o");
    else
        brk = new QTableWidgetItem("");
    QTableWidgetItem *ins = new QTableWidgetItem(QString("$%1").arg(instruction,8,16,QChar(QLatin1Char('0'))));
    QTableWidgetItem *nam = new QTableWidgetItem(P8x32a::getInstName(ins_op, ins_zcri, ins_cnd, ins_src));
    //QTableWidgetItem *op  = new QTableWidgetItem(QString("$%1").arg(ins_op,2,16,QChar(QLatin1Char('0'))));
    QTableWidgetItem *cnd = new QTableWidgetItem(P8x32a::getCondition(ins_cnd));
    QTableWidgetItem *wz  = new QTableWidgetItem(QString("%1").arg(ins_wz,1,2));
    QTableWidgetItem *wc  = new QTableWidgetItem(QString("%1").arg(ins_wc,1,2));
    QTableWidgetItem *wr  = new QTableWidgetItem(QString("%1").arg(ins_wr,1,2));
    QTableWidgetItem *dst = new QTableWidgetItem(QString("$%1").arg(ins_dst,3,16,QChar(QLatin1Char('0'))));
    //QTableWidgetItem *imm = new QTableWidgetItem(ins_imm ? "#" : "");
    QTableWidgetItem *src;
    if(ins_imm)
        src = new QTableWidgetItem(QString("#$%1").arg(ins_src,3,16,QChar(QLatin1Char('0'))));
    else
        src = new QTableWidgetItem(QString("$%1").arg(ins_src,3,16,QChar(QLatin1Char('0'))));
    QTableWidgetItem *z   = new QTableWidgetItem(zres);
    QTableWidgetItem *c   = new QTableWidgetItem(cres);
    QTableWidgetItem *res = new QTableWidgetItem(dres);

    brk->setTextAlignment(Qt::AlignCenter);
    ins->setTextAlignment(Qt::AlignCenter);
    nam->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);
    //op->setTextAlignment(Qt::AlignCenter);
    cnd->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);
    wz->setTextAlignment(Qt::AlignCenter);
    wc->setTextAlignment(Qt::AlignCenter);
    wr->setTextAlignment(Qt::AlignCenter);
    dst->setTextAlignment(Qt::AlignCenter);
    //imm->setTextAlignment(Qt::AlignCenter);
    src->setTextAlignment(Qt::AlignCenter);
    z->setTextAlignment(Qt::AlignCenter);
    c->setTextAlignment(Qt::AlignCenter);
    res->setTextAlignment(Qt::AlignCenter);

    // don't change brk. highlighting breaks otherwise //
    ins->setFlags(Qt::ItemIsEnabled);
    nam->setFlags(Qt::ItemIsEnabled);
    //op->setFlags(Qt::ItemIsEnabled);
    cnd->setFlags(Qt::ItemIsEnabled);
    wz->setFlags(Qt::ItemIsEnabled);
    wc->setFlags(Qt::ItemIsEnabled);
    wr->setFlags(Qt::ItemIsEnabled);
    dst->setFlags(Qt::ItemIsEnabled);
    //imm->setFlags(Qt::ItemIsEnabled);
    src->setFlags(Qt::ItemIsEnabled);
    z->setFlags(Qt::ItemIsEnabled);
    c->setFlags(Qt::ItemIsEnabled);
    res->setFlags(Qt::ItemIsEnabled);

    brk->setToolTip(tr("Click to set breakpoint"));
    ins->setToolTip(tr("Machine code instruction"));
    nam->setToolTip(tr("Assembly instruction name"));
    //op->setToolTip(tr("Instruction opcode"));
    cnd->setToolTip(tr("Condition for execution."));
    wz->setToolTip(tr("If set, instruction writes Zero flag."));
    wc->setToolTip(tr("If set, instruction writes Carry flag."));
    wr->setToolTip(tr("If set, instruction writes result to DST."));
    z->setToolTip(tr("Zero flag instruction result."));
    c->setToolTip(tr("Carry flag instruction result."));
    res->setToolTip(tr("Result written to destination."));

    QStringList regvals;
    regvals.append(parValue.text());
    regvals.append(cntValue.text());
    regvals.append(inaValue.text());
    regvals.append(inbValue.text());
    regvals.append(outaValue.text());
    regvals.append(outbValue.text());
    regvals.append(diraValue.text());
    regvals.append(dirbValue.text());
    regvals.append(ctraValue.text());
    regvals.append(ctrbValue.text());
    regvals.append(frqaValue.text());
    regvals.append(frqbValue.text());
    regvals.append(phsaValue.text());
    regvals.append(phsbValue.text());
    regvals.append(vcfgValue.text());
    regvals.append(vsclValue.text());

#if 1
    dst->setToolTip(tr("Destination register."));
    src->setToolTip(tr("Source register."));
#else
    if(ins_dst < P8x32a::COGCODESIZE) {
        if(info != 0)
            dst->setToolTip(tr("Destination start ")+QString("$%1").arg(info->dstval,8,16,QChar(QLatin1Char('0'))));
        else
            dst->setToolTip(tr("Destination ")+QString("$%1").arg(codeList.at(ins_dst),8,16,QChar(QLatin1Char('0'))));
    } else {
        QString reg = regvals.at((ins_dst-P8x32a::COGCODESIZE) & 0xF);
        dst->setToolTip(tr("Destination invalid"));
    }

    //imm->setToolTip(tr("If set, user SRC#, else use value at SRC."));
    if(ins_imm) {
        src->setToolTip(tr("Source register. ")+QString("#$%1").arg(ins_src,3,16,QChar(QLatin1Char('0'))));
    }
    else {
        if(ins_src < P8x32a::COGCODESIZE) {
            if(info != 0)
                src->setToolTip(tr("Source start ")+QString("$%1").arg(info->srcval,8,16,QChar(QLatin1Char('0'))));
            else
                src->setToolTip(tr("Source ")+QString("$%1").arg(codeList.at(ins_src),8,16,QChar(QLatin1Char('0'))));
        } else {
            QString reg = regvals.at((ins_src-P8x32a::COGCODESIZE) & 0xF);
            src->setToolTip(tr("Source invalid")+reg);
        }
    }
#endif
    code.setItem(pc,CODE_BRK,brk);
    code.setItem(pc,CODE_INS,ins);
    code.setItem(pc,CODE_NAME,nam);
    //code.setItem(pc,CODE_OP,op);
    code.setItem(pc,CODE_CND,cnd);
    code.setItem(pc,CODE_WZ,wz);
    code.setItem(pc,CODE_WC,wc);
    code.setItem(pc,CODE_WR,wr);
    code.setItem(pc,CODE_DST,dst);
    //code.setItem(pc,CODE_IMM,imm);
    code.setItem(pc,CODE_SRC,src);
    code.setItem(pc,CODE_ZRES,z);
    code.setItem(pc,CODE_CRES,c);
    code.setItem(pc,CODE_DRES,res);
}

void P8x32aCogPanel::showCogPC(int pc)
{
    code.selectRow(pc);
}

void P8x32aCogPanel::showCog(CogInfo *info)
{
    pcValue.setText(QString("%1").arg(info->pc,8,16,QChar(QLatin1Char('0'))));
    insValue.setText(QString("%1").arg(info->ins,8,16,QChar(QLatin1Char('0'))));
    cndValue.setText(QString("%1").arg(info->cond,8,16,QChar(QLatin1Char('0'))));
    zValue.setText(QString("%1").arg(info->z));
    cValue.setText(QString("%1").arg(info->c));
    srcValue.setText(QString("%1").arg(info->src,3,16,QChar(QLatin1Char('0'))));
    dstValue.setText(QString("%1").arg(info->dst,3,16,QChar(QLatin1Char('0'))));
    resValue.setText(QString("%1").arg(info->result,8,16,QChar(QLatin1Char('0'))));

    parValue.setText(QString("%1").arg(info->PAR,8,16,QChar(QLatin1Char('0'))));
    cntValue.setText(QString("%1").arg(info->CNT,8,16,QChar(QLatin1Char('0'))));
    inaValue.setText(QString("%1").arg(info->INA,8,16,QChar(QLatin1Char('0'))));
    inbValue.setText(QString("%1").arg(info->INB,8,16,QChar(QLatin1Char('0'))));
    outaValue.setText(QString("%1").arg(info->OUTA,8,16,QChar(QLatin1Char('0'))));
    outbValue.setText(QString("%1").arg(info->OUTB,8,16,QChar(QLatin1Char('0'))));
    diraValue.setText(QString("%1").arg(info->DIRA,8,16,QChar(QLatin1Char('0'))));
    dirbValue.setText(QString("%1").arg(info->DIRB,8,16,QChar(QLatin1Char('0'))));
    ctraValue.setText(QString("%1").arg(info->CTRA,8,16,QChar(QLatin1Char('0'))));
    ctrbValue.setText(QString("%1").arg(info->CTRB,8,16,QChar(QLatin1Char('0'))));
    frqaValue.setText(QString("%1").arg(info->FRQA,8,16,QChar(QLatin1Char('0'))));
    frqbValue.setText(QString("%1").arg(info->FRQB,8,16,QChar(QLatin1Char('0'))));
    phsaValue.setText(QString("%1").arg(info->PHSA,8,16,QChar(QLatin1Char('0'))));
    phsbValue.setText(QString("%1").arg(info->PHSB,8,16,QChar(QLatin1Char('0'))));
    vcfgValue.setText(QString("%1").arg(info->VCFG,8,16,QChar(QLatin1Char('0'))));
    vsclValue.setText(QString("%1").arg(info->VSCL,8,16,QChar(QLatin1Char('0'))));

    parValue.setAlignment(Qt::AlignCenter);
    cntValue.setAlignment(Qt::AlignCenter);
    inaValue.setAlignment(Qt::AlignCenter);
    inbValue.setAlignment(Qt::AlignCenter);
    outaValue.setAlignment(Qt::AlignCenter);
    outbValue.setAlignment(Qt::AlignCenter);
    diraValue.setAlignment(Qt::AlignCenter);
    dirbValue.setAlignment(Qt::AlignCenter);
    ctraValue.setAlignment(Qt::AlignCenter);
    ctrbValue.setAlignment(Qt::AlignCenter);
    frqaValue.setAlignment(Qt::AlignCenter);
    frqbValue.setAlignment(Qt::AlignCenter);
    phsaValue.setAlignment(Qt::AlignCenter);
    phsbValue.setAlignment(Qt::AlignCenter);
    vcfgValue.setAlignment(Qt::AlignCenter);
    vsclValue.setAlignment(Qt::AlignCenter);

    code.selectRow(info->pc);
    setCodeLine(info->pc, info->ins, info->codeList, info);
    if(info->wr) {
        setCodeLine(info->dst, info->result, info->codeList);
    }
}
